package midas.mw;

/*
 * History:
 * 2007-08-15: Component startup sequence corrected (ERR_003), and stop sequence (see prev release note)
 */

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Inet4Address;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.Vector;

import midas.mw.cam.interfaces.Cam;
import midas.mw.cfg.CfgError;
import midas.mw.cfg.CfgLogging;
import midas.mw.cfg.interfaces.RegisteredComponent;
import midas.mw.cfg.log.Logger;
import midas.mw.cfg.mwmonitor.CFGIllegalOperationException;
import midas.mw.cfg.mwmonitor.MWMonitor;
import midas.mw.cfg.system.CFGNotSupported;
import midas.mw.cfg.system.MWSystem;
import midas.mw.ckb.interfaces.Ckb;
import midas.mw.crt.interfaces.Crt;
import midas.mw.ctx.interfaces.Ctx;
import midas.mw.dpl.interfaces.Dpl;
import midas.mw.interfaces.Application;
import midas.mw.interfaces.Component;
import midas.mw.interfaces.Mw;
import midas.mw.mds.interfaces.Mds;

/**
 * This defines the main MIDAS MW_Core implementation class. The class implements the
 * local MW_core API. The provided functionality is to start and stop 
 * the middleware core components and to provide access to started components.
 */
public class MwImpl extends Thread implements Mw, Runnable {
	/** Current component version. Use versioning rules for update 
	 * - mw.3.0.0: First MW 1.0 version
	 *   MW_Core: New component DPL supported
	 *   RAPI: Changes for all(?) MW components
	 * - mwx.0.0.1: New variant of the MW_Core, where RAPI is excluded and application launcher is included
	 *   Functional change: Each components is now created and started, while earlier all component 
	 *   were first created and then started
	 *   mwx.0.1.0: Added the 'Application' interface which the applications must implement
	 *   Removed inclusion of the TEST component
	 *   mwx.0.2.0: - Adapted to CFG extention as defined in mail ...
	 *   	- Corrected error in parsing configuration properties
	 *   mw.4.0.0: Prepared for MW 1.2 version
	 *   mw.4.1.0: Corrected errors
	 *   - issue artf300650 - reported by UiO concerning infinite loop when reading control command from env.
	 *   - MWSystem.initialize() failure not defined as fatal
	 *   - Removed file 'mwStartH2'
	 *   mw.4.2.0: Corrected errors
	 *   - Close server socket on termination
	 *   - Changed mwControl ($0 $1 ... -> $@
	 *   mw.4.4.0: No changes, only component version list updated
	 *   */
	
	/** The MW_Core version no */
	public static final String mwVersion = "mwx.4.4.0";	
	/** Set of middleware controlled running components */
	private Hashtable runningComponents = new Hashtable();	
	/** Set of middleware controlled running applications */
	private Hashtable runningApplications = new Hashtable();
	/** Requested component to start. Used to communicate main class name from Rhread start() to run() */
	private MwRunningComponent componentToStart = null;
	/** Defines the components to be created, started and stopped, and the sequence to start/stop */
	private Vector components = new Vector();;
	/** Define runLevel, i.e. which components will be started. */
	private int runLevel;
	/** Trace level to be used */
	private String traceLevel;
	/** Local port number to be used for Socket communication between 'MwControl' and 'MwImpl' classes */
	private int serverSocketPortNo;
	/** Local IP address to be used for Socket communication between 'MwControl' and 'MwImpl' classes */
	private String serverSocketIpAddress;
	/** Registration as a comonent in MWMonitor */
	private RegisteredComponent registeredComponent;
	/** Server socket to use for control messages */
	private ServerSocket serverSocket = null;
	/** Interface to CRT */
	private Crt crt = null;
	/** Interface to MDS */
	private Mds mds = null;
	/** Interface to CTX */
	private Ctx ctx = null;
	/** Interface to CAM */
	private Cam cam = null;
	/** Interface to CKB */
	private Ckb ckb = null;
	/** Interface to DPL */
	private Dpl dpl = null;
	
	/**
	 * Constructor - initiate local variables
	 */	
	private MwImpl () {
		if (Common.DEBUG) CfgLogging.logEvent(CfgLogging.LOG_MCALL, "MW_Core", CfgLogging.SEV_DEBUG, "MwImpl()");
		
		/*NOTE: Not used
		//Add a shutdown hook to be able to have a controlled shutdown (will call 'run()' at exit)
		Runtime.getRuntime().addShutdownHook(new Thread() {
			public void run() {
				stopMiddleware();
			}
		});
		*/
	}
	
	/**
	 * Perform startup of the middleware
	 * @return 0: Startup ok, else failure
	 */
	private int startMiddleware() {
		//The CFG component is not dynamically created. Initiate it before starting the others components
		Logger.initialize(Common.LOGGER_CONFIGURATION_FILE);			
		try {
			MWSystem.initialize();		
		} catch (CFGNotSupported e) {
			CfgError.errorReporting(CfgError.ERR_INFO, "MW_Core", CfgError.SEV_ERROR,
					"MWSystem.initialize() failed, error: "+e.getMessage(), null);	
			/* NOTE: Maybe this should be FATAL
			return -2;
			*/
		}
		MWMonitor.initialize();
		
		//Register MW_Core component
		try {
			registeredComponent = MWMonitor.cfgRegisterComponent(MWMonitor.COMPONENT_MW_Core);
		} catch (CFGIllegalOperationException e) {
			CfgError.errorReporting(CfgError.ERR_INFO, "MW_Core", CfgError.SEV_ERROR,
					"MWMonitor.cfgRegisterComponent failed, error ("+e.getMessage()+")", null);						
			stopMiddleware();
			return -3;			
		}
		
		//Set component status
		registeredComponent.cfgSetComponentStatus(MWMonitor.STATUS_STARTING);
		
		if (Common.DEBUG) CfgLogging.logEvent(CfgLogging.LOG_MCALL, "MW_Core", CfgLogging.SEV_DEBUG, "startMiddleware()");
		
		//Configure the middleware
		if (configure() < 0) {
			//Failed to configure - terminate
			stopMiddleware();
			return -1;
		}
		
		// Create and start the different middleware components
		CfgLogging.logEvent(CfgLogging.LOG_INFO, "MW_Core", CfgLogging.SEV_INFORMAL, "Create and start middleware components");

		//Setup server socket to be used for reveiving commands, e.g. starting application
		try {
			serverSocket = new ServerSocket(serverSocketPortNo, 1, Inet4Address.getByName(serverSocketIpAddress));
//			connection = serverSocket.accept();
		} catch (IOException e) {
			CfgError.errorReporting(CfgError.ERR_OPER, "MW_Core", CfgError.SEV_FATAL, 
					"Failed to create server socket on port '"+serverSocketPortNo+"' and IP '" + serverSocketIpAddress + "' '"+ e.getMessage() + "'", null);
			stopMiddleware();
			return -5;
		}
		
		if (createAndStartMwComponents(runningComponents, components) < 0) {
			//Failed to start. Started components has been terminated
			stopMiddleware();
			return -4;
		}
		
		//If no components are started, just wait 10 seconds and terminate the middleware.
		if (runLevel == 0) {
			//No components have been started. Wait 10 seconds before termination
			//to be able to verify e.g. JVM process size
			CfgLogging.logEvent(CfgLogging.LOG_INFO, "MW_Core", CfgLogging.SEV_INFORMAL,
					"No components started due to runLevel set to 0. Terminates after 10 seconds");		
			try {
				Thread.sleep(10*1000);
			} catch (Exception e) {
				CfgError.errorReporting(CfgError.ERR_INFO, "MW_Core", CfgError.SEV_WARNING,
						"Failed to wait 10 seconds", null);						
			};
			return 0;
		}
		
		CfgLogging.logEvent(CfgLogging.LOG_INFO, "MW_Core", CfgLogging.SEV_INFORMAL, "startMiddleware() - completed successfully");		

		//Update component status
		registeredComponent.cfgSetComponentStatus(MWMonitor.STATUS_STARTED);
				
		/*
		 * When middleware components have sucessfully started the middleware control 
		 * continues its execution as a waiting for operation requests to start applications
		 * or to terminate the middleware.
		 */
		
		//Continue execution of the following loop until 'running' set to 'false' by someone
		boolean running = true;
		while (running) {
			//Wait until receiption of message from the middleware environments
			String message = waitNextMessage();
			if (Common.DEBUG) CfgLogging.logEvent(CfgLogging.LOG_INFO, "MW_Core", CfgLogging.SEV_DEBUG, 
					"Received operation request '"+message+"'");
			
			//Interpret the message and take action
			Vector v = new Vector();
			if (message != null) {
				//Decompose received message into space separated tokens
				StringTokenizer st = new StringTokenizer(message);
				while (st.hasMoreTokens()) {
					v.add(st.nextToken());
				}
				
			    if (v.size() > 0) {
			    	//Message is not empty, get command to middleware
				    String cmd = (String)v.get(0);
					if (cmd.equalsIgnoreCase("START")) {
						//Get class to start and all optional arguments
						if (v.size() > 1) {
							//At least class name exist as well
							String cl = (String)v.get(1);
							Enumeration e = v.elements();
							//Ignore first two 
							e.nextElement(); 
							e.nextElement();
							//Allocate space for arguments
							String a[] = new String[v.size() - 2];
							int i = 0;
							for (; e.hasMoreElements();) {
								a[i] = (String) e.nextElement();
								++i;
							}
							
							//Execute operation. Error handling is done in start method
							startComponentAsSepThread(cl , a);
						}
					} else if (cmd.equalsIgnoreCase("TERMINATE")) {
						//Middleware termination command received. No more applications
						running = false;
					} else if (cmd.equalsIgnoreCase("TRACE")) {
						//Set trace level if parameer provided
						if (v.size() >= 2) {
							String s = (String) v.get(1);
							setTraceLevel(s);
						}
					} else if (cmd.equalsIgnoreCase("ERROR")) {
						//Failed to receive command - ignore
					}
			    }
			}
		}
		
		//Ends out here if middleware requested to be stopped
		stopMiddleware();
			
		return 0;
	}
	
	/** List of all MW components and start sequence index 0 first */
	private String allMwComponents[] = {
			"midas.mw.crt.CrtImpl", 
			"midas.mw.mds.MdsImpl", 
			"midas.mw.ckb.CkbImpl", 
			"midas.mw.cam.CamImpl", 
			"midas.mw.ctx.CtxImpl", 
			"midas.mw.dpl.DplImpl" 
			};

	/**
	 * Create and start components defined in 'allMwComponents[]'
	 * @param runningComponents Component names and access points
	 * @param components Component name and start sequnce
	 * @return
	 */
	private int createAndStartMwComponents(Hashtable runningComponents, Vector components) {
		for (int i = 0; i < allMwComponents.length; ++i) {
			String compName = allMwComponents[i];
			if ((i + 2) <= runLevel) {
				Component component 	= createComponent(compName);
		
				if (component == null) 
				{
					//The component can't be created. The Middleware is stopped 
					stopMiddleware();
					return -1;
				}
		
				//This component has to be included in the hashtable storing the running components
				runningComponents.put(compName, component);
				components.add(compName);
				
				//Set direct access address
				switch (i) {
				case 0: crt = (Crt) component; break;
				case 1: mds = (Mds) component; break;
				case 2: ckb = (Ckb) component; break;
				case 3: cam = (Cam) component; break;
				case 4: ctx = (Ctx) component; break;
				case 5: dpl = (Dpl) component; break;
				}
				
				if (component.start(this) != 0) {
					CfgError.errorReporting(CfgError.ERR_INFO, "MW_Core", CfgError.SEV_FATAL, 
							"Failed to start middleware component '"+compName+"'", null);
					/*NOTE: How to be used
					registeredComponent.cfgFireError(MWMonitor.SEVERITY_FATAL, 
							"Failed to start middleware component '"+compName+"'", null);
							*/
					stopMiddleware();
					return -1;
					
				}
			}
		}
		
		return 0;
	}
		
	/**
	 * Used to receive commands from its envorments. The messages are communicated
	 * using socket on port 'ServerSocketNo'. (Probably a temporary solution).
	 * @return 'null': No message, else received message
	 */
	private String waitNextMessage() {
		
		String readMessage = null;
		Socket connection = null;
		try {
			connection = serverSocket.accept();
				
			InputStream in = connection.getInputStream();
			BufferedReader bin = new BufferedReader(new InputStreamReader(in));
			readMessage = bin.readLine();
				
			bin.close();
			connection.close();
		} catch (IOException e) {
			//Ignore
			CfgError.errorReporting(CfgError.ERR_OPER, "MW_Core", CfgError.SEV_WARNING, 
					"waitNextMessage() Socket timeout '"+ e.getMessage() + "'", null);
			readMessage = "ERROR";
		}
		finally {
			try {
				if (connection != null) {
					connection.close();
				}
			}
			catch (IOException ex) {}
		}
		
		return readMessage;		
	}
	
	/**
	 * Request start a component - may be middleware component or application 
	 * A new thread is started and the component is loaded and starts its execution
	 * in that thread. The current thread continues its execution after a 100ms delay.<p>
	 * 
	 * @param compClassName Binary name of the component main class
	 * @param args The list of actual arguments following the method call
	 * @return 'null': Ok; else failed to start indicating type of failure
	 */
	private int startComponentAsSepThread(String compClassName, String args[]) {
		if (Common.DEBUG) CfgLogging.logEvent(CfgLogging.LOG_MCALL, "MW_Core", CfgLogging.SEV_DEBUG, 
				"startComponentAsSepThread("+compClassName+", "+args+")");
		
		//Run this component as a separate thread
		Thread mwThread = new Thread(this);
		componentToStart = new MwRunningComponent(compClassName, args, mwThread.getName(), mwThread);

		//Start the thread. The new thread start execution at 'run()' entry point
		synchronized (this) {
			mwThread.start();
		}
		
		//The thread where started from continues the execution here
		try {
			//Sleep 100ms to ensure component startup completes
			Thread.sleep(100);
		} catch (Exception e) {
			CfgError.errorReporting(CfgError.ERR_OPER, "MW_Core", CfgError.SEV_WARNING, 
					"startComponentAsSepThread() Failed to sleep at component startup '"+ 
					e.getMessage() + "'", null);
			return -1;
		}

		//Sucessfully completed
		return 0;
	}
	
	/*
	 * This method must be implemented when using thread. It is defined in the 
	 * 'java.lang.Runnable' interface.<p>
	 * The method is executed when a new component has been requested to be started.
	 * A new thread for executing the component independent of the middleware execution
	 * thread has been created and its initial entry point is at 'run()'.
	 * 'componentToStart is used to communicate main class name for class to start
	 */
	public void run() {
		//The application to be started begins its life here, defined in the object componentToStart
		MwRunningComponent rc = componentToStart;
		componentToStart = null;
		Application cc = loadAndInvokeClass(rc);
		
		if (cc == null) 
			//Failed to prepare for running application
			return;
		
		//Keep information about the stated component (assuming that every things goes ok)
		rc.setObject(cc);
		runningApplications.put(rc.getThreadName(), rc);
		
		if (Common.DEBUG) CfgLogging.logEvent(CfgLogging.LOG_INFO, "MW_Core", CfgLogging.SEV_DEBUG, 
				"Starting application '"+rc.getCompClassName()+"' in thread '"+rc.getThreadName()+"'");
		int res = cc.start(this, rc.getArgs());
		if (res != 0) {
			CfgError.errorReporting(CfgError.ERR_OPER, "MW_Core", CfgError.SEV_ERROR, 
					"run() Execution of application '"+ rc.getCompClassName() + ".start()' failed, errcode="+res, null);
		}
		
		//Ends out here when thread created for the component terminates.
		//Note that the component object may still be active, executed by other threads.
		//NOTE: At this point middleware are not able to determine whether or not the object has terminated
		
		runningApplications.remove(rc.getThreadName());
		
		CfgLogging.logEvent(CfgLogging.LOG_INFO, "MW_Core", CfgLogging.SEV_INFORMAL, 
				"Terminated application '"+rc.getCompClassName()+"' in thread '"+rc.getThreadName()+"'");
		
	}
	
	/**
	 * This function loads and invokes a component requested to be started
	 * @param compClassName The class to start
	 * @return 0:Ok, <0:Failed
	 */
	private Application loadAndInvokeClass(MwRunningComponent componentToStart) {
		
		MwRunningComponent rc = componentToStart;
		//Reset 'global' variable to ensure not used again
		componentToStart = null;
		
		//Instantiated object to be invoked
		Object obj = null;
		
		//The component's main class
		Class main = null;
		
		try {
			//Load main class, and all its dependent classes using the MIDAS class loaded
		   MwClassLoader loader = new MwClassLoader();
		   main = loader.loadClass(rc.getCompClassName(), true);
		   
		   if (main == null) {
				CfgError.errorReporting(CfgError.ERR_OPER, "MW_Core", CfgError.SEV_ERROR, 
						"loadAndInvokeClass() Failed to load application class  '"+ 
						rc.getCompClassName() + "'", null);
			   return null;
		   }
		   
		   //Instantiate the main class
		   obj = main.newInstance();
		   
		} catch (IllegalAccessException e) {
			CfgError.errorReporting(CfgError.ERR_OPER, "MW_Core", CfgError.SEV_ERROR, 
					"loadAndInvokeClass() Failed to create object instance (IllegalAccessException)'"+ 
					e.getMessage() + "'", null);
			e.printStackTrace();
			return null;
		} catch (InstantiationException e) {
			CfgError.errorReporting(CfgError.ERR_OPER, "MW_Core", CfgError.SEV_ERROR, 
					"loadAndInvokeClass() Failed to create object instance (InstantiationException)'"+ 
					e.getMessage() + "'", null);
			return null;
		};
		
		return (Application) obj;
	}
	
	/**
	 * Create a middleware component 
	 * @param compClassName Binary name of the component main class
	 * @return 'null': Ok; else failed to start indicating type of failure
	 */
	private Component createComponent(String compClassName) {
		if (Common.DEBUG) CfgLogging.logEvent(CfgLogging.LOG_MCALL, "MW_Core", CfgLogging.SEV_DEBUG, "createComponent(" + compClassName + ")");
				
		//The component's main class
		Class main = null;
		
		//Make the component null to avoid having another component's interface
		Component component 	= null;
		try {
			//Load main class, and all its dependent classes using the MIDAS class loaded
		   MwClassLoader loader = new MwClassLoader();		   
		   main = loader.loadClass(compClassName, true);		   
		   //Instantiate the main class		   
		   component = (Component)main.newInstance();		  
		   
		} catch (IllegalAccessException e) {			
			CfgError.errorReporting(CfgError.ERR_OPER, "MW_Core", CfgError.SEV_FATAL, 
					"createComponent(" + compClassName + ") Failed to create object instance (IllegalAccessException) '"+ 
					e.getMessage() + "'", null);
			e.printStackTrace();
		} catch (InstantiationException e) {			
			CfgError.errorReporting(CfgError.ERR_OPER, "MW_Core", CfgError.SEV_FATAL, 
					"createComponent(" + compClassName + ") Failed to create object instance (InstantiationException) '"+ 
					e.getMessage() + "'", null);			
			e.printStackTrace();
		}
		
		return component;
	}
	
	/**
	 * Perform necessary configuration of the middleware. Configuration parameters are read
	 * from the file defined by the variable 'CONFIGURATION_FILE'.
	 * @return 0: No error; -1:Error exit
	 */
	private int configure() {
		if (Common.DEBUG) CfgLogging.logEvent(CfgLogging.LOG_MCALL, "MW_Core", CfgLogging.SEV_DEBUG, "configure()");
		
		Properties middlewareProperties;

	    /* Loading the properties file
	     */
	    try 
	    {	    	
	    	FileInputStream file = new FileInputStream(Common.CONFIGURATION_FILE);

	    	middlewareProperties = new Properties();
	    	middlewareProperties.load(file);
	    	file.close();
	    	
	    	//Set trace level	 
	    	traceLevel = middlewareProperties.getProperty("TraceLevel", Common.DEFAULT_TRACE_LEVEL);
	    	setTraceLevel(traceLevel);
			CfgLogging.logEvent(CfgLogging.LOG_INFO, "MW_Core", CfgLogging.SEV_INFORMAL, "PROPERTY TraceLevel = " + traceLevel);
	    	
	    	//Set Server Socket communication port no	 
	    	serverSocketPortNo = Integer.parseInt(middlewareProperties.getProperty("ServerSocketPortNo", Common.DEFAULT_SERVER_SOCKET_PORT_NO));
			CfgLogging.logEvent(CfgLogging.LOG_INFO, "MW_Core", CfgLogging.SEV_INFORMAL, "PROPERTY ServerSocketPortNo = " + serverSocketPortNo);
			
			serverSocketIpAddress = middlewareProperties.getProperty("ServerSocketIpAddress", Common.DEFAULT_SERVER_SOCKET_IP_ADDRESS);
			CfgLogging.logEvent(CfgLogging.LOG_INFO, "MW_Core", CfgLogging.SEV_INFORMAL, "PROPERTY ServerSocketIpAddress = " + serverSocketIpAddress);
	    	
	    	//Set run configuration	 
	    	runLevel = Integer.parseInt(middlewareProperties.getProperty("RunLevel", Common.DEFAULT_RUN_LEVEL));
			CfgLogging.logEvent(CfgLogging.LOG_INFO, "MW_Core", CfgLogging.SEV_INFORMAL, "PROPERTY RunLevel = " + runLevel);
	    	
	    } catch (FileNotFoundException fnfe) {
	    	CfgError.errorReporting(CfgError.ERR_OPER, "MW_Core", CfgError.SEV_ERROR, 
					"configure(). Failed to get the Properties file ' (FileNotFoundException)" + 
					fnfe.getMessage() + "'", null);
	      	return -1;
	    } catch (IOException ioe) {
	    	CfgError.errorReporting(CfgError.ERR_OPER, "MW_Core", CfgError.SEV_ERROR, 
					"configure(). Failed to read the Properties file ' (IOException)" + 
					ioe.getMessage() + "'", null);
	    	return -1;
	    }
	    
		CfgLogging.logEvent(CfgLogging.LOG_MCALL, "MW_Core", CfgLogging.SEV_INFORMAL, "configure() - completed");
		
	    return 0;
	}
	
	private void setTraceLevel(String level) {
		int numLevel = CfgLogging.SEV_INFORMAL;
		if (level == null)
			return;
		if (level.equalsIgnoreCase("DEBUG"))
			numLevel = CfgLogging.SEV_DEBUG;	// 10000
		else if (level.equalsIgnoreCase("INFO"))
			numLevel = CfgLogging.SEV_INFORMAL;	// 20000
		else if (level.equalsIgnoreCase("WARN"))
			numLevel = CfgLogging.SEV_WARNING;	//30000
		else if (level.equalsIgnoreCase("ERROR"))
			numLevel = CfgLogging.SEV_ERROR;	//40000
		else if (level.equalsIgnoreCase("FATAL"))
			numLevel = CfgLogging.SEV_FATAL;	//50000
		String comps[] = {"MW_Core", "CRT", "MDS", "CKB", "CAM", "CTX", "DPL"};
		for (int i = 0; i < comps.length; ++i) {
			CfgLogging.setCurrentLevel(comps[i], numLevel);
			/* Initiate one is sufficient
			CfgError.setCurrentLevel(comps[i], numLevel);
			*/
		}

	}
	
	/**
	 * Stop the running components (calling their 'mwStop()' methods) and terminate.
	 * This is currently not available through the API or RAPI. It is only used
	 * if startup fails.
	 * @return 0 in any case
	 */
	public int stopMiddleware() {		
		if (Common.DEBUG) CfgLogging.logEvent(CfgLogging.LOG_MCALL, "MW_Core", CfgLogging.SEV_DEBUG, "stopMiddleware()");
		
		//Terminate server socket if used
		if (serverSocket != null)
			try {
				serverSocket.close();
			} catch (Exception e) {};
		
		//Terminate applications first
		for (Enumeration e = runningApplications.elements(); e.hasMoreElements();) {
			MwRunningComponent rc = (MwRunningComponent) e.nextElement();
			Application c = (Application) rc.getObject();
			c.stop();
		}
		runningApplications.clear();


		int ii = components.size();
		while (ii > 0) {
			--ii;
			String compName = (String) components.elementAt(ii);
			Component component = (Component) runningComponents.get(compName);
			if (component != null) {
				//Component exists and shall be stopped
				if (Common.DEBUG) CfgLogging.logEvent(CfgLogging.LOG_INFO, "MW_Core", CfgLogging.SEV_DEBUG, "Stopping component '"+compName+"'");
				int res = component.stop();
				if (res != 0)
					CfgError.errorReporting(CfgLogging.LOG_INFO, "MW_Core", CfgLogging.SEV_WARNING, 
							"Error in running 'stop()' for component '"+compName+"', errno=" + res, null);
			}
		}
		runningComponents.clear();	
		
		CfgLogging.logEvent(CfgLogging.LOG_INFO, "MW_Core", CfgLogging.SEV_INFORMAL, "stopMiddleware() - completed");
		return 0;
	}
	
	/*
	 * Implements the provided API, see 'Mw.java'
	 */
	
	public void stopMw() {
		if (Common.DEBUG) CfgLogging.logEvent(CfgLogging.LOG_MCALL, "MW_Core", CfgLogging.SEV_DEBUG, "stopMw()");		
		stopMiddleware();
		
		try {
			//Wait
			Thread.sleep(500);
		} catch (Exception e) {}
		
		System.exit(0);
	}
	
	public Crt getCrt()
	{
		if (Common.DEBUG) CfgLogging.logEvent(CfgLogging.LOG_API, "MW_Core", CfgLogging.SEV_DEBUG, "getCrt()");		
		return crt;
	}
	
	public Mds getMds()
	{
		if (Common.DEBUG) CfgLogging.logEvent(CfgLogging.LOG_API, "MW_Core", CfgLogging.SEV_DEBUG, "getMds()");		
		return mds;
	}
	
	public Ctx getCtx()
	{
		if (Common.DEBUG) CfgLogging.logEvent(CfgLogging.LOG_API, "MW_Core", CfgLogging.SEV_DEBUG, "getCtx()");		
		return ctx;
	}

	public Ckb getCkb()
	{
		if (Common.DEBUG) CfgLogging.logEvent(CfgLogging.LOG_API, "MW_Core", CfgLogging.SEV_DEBUG, "getCkb()");		
		return ckb;
	}

	public Cam getCam()
	{
		if (Common.DEBUG) CfgLogging.logEvent(CfgLogging.LOG_API, "MW_Core", CfgLogging.SEV_DEBUG, "getCam()");		
		return cam;
	}

	public Dpl getDpl()
	{
		if (Common.DEBUG) CfgLogging.logEvent(CfgLogging.LOG_API, "MW_Core", CfgLogging.SEV_DEBUG, "getDpl()");		
		return dpl;
	}

	public Hashtable getRunningComponents()
    {
		if (Common.DEBUG) CfgLogging.logEvent(CfgLogging.LOG_API, "MW_Core", CfgLogging.SEV_DEBUG, "getRunningComponents()");		
		return runningComponents;
    }
	
	public Component getRunningComponent(String componentName) {
		if (Common.DEBUG) CfgLogging.logEvent(CfgLogging.LOG_API, "MW_Core", CfgLogging.SEV_DEBUG, 
				"getRunningComponent("+componentName+")");	

		if (componentName == null) {
			CfgError.errorReporting(CfgError.ERR_OPER, "MW_Core", CfgError.SEV_ERROR, 
					"MwImpl.getRunningComponent() - Missing parameter value", null);
			
			return null;
		}
		
		//NOTE:TEMPORARY only. API specified that the whole component name shall be used
		if (componentName.equals("CRT")) {
			return (Component) runningComponents.get("midas.mw.crt.CrtImpl");
		} else if (componentName.equals("MDS")) {
			return (Component) runningComponents.get("midas.mw.mds.MdsImpl");
		} else if (componentName.equals("CTX")) {
			return (Component) runningComponents.get("midas.mw.ctx.CtxImpl");
		} else if (componentName.equals("CAM")) {
			return (Component) runningComponents.get("midas.mw.cam.CamImpl");
		} else if (componentName.equals("CKB")) {
			return (Component) runningComponents.get("midas.mw.ckb.CkbImpl");
		} else if (componentName.equals("DPL")) {
			return (Component) runningComponents.get("midas.mw.dpl.DplImpl");
		} else {
			return (Component) runningComponents.get(componentName);
		}
	}
	
	/**
	 * Get MW_Core version no
	 */
	public String getVersion() {
		CfgLogging.logEvent(CfgLogging.LOG_API, "MW_Core", CfgLogging.SEV_INFORMAL, "getVersion()");	
		return mwVersion;
	}	
	
	/**
	 * The main program for starting the middleware. Terminates if startup fails.
	 * @param args Arguments are not used
	 */
	public static void main(String[] args) {
		
		if (Common.DEBUG) CfgLogging.logEvent(CfgLogging.LOG_MCALL, "MW_Core", CfgLogging.SEV_DEBUG, "main(<args>)");
		
		if (args.length >= 1 && args[0].equals("-v")) {
			System.out.println("Versions: MW(7.4.0), MW_Core(4.4.0), CAM(3.0.0), CFG(1.2a), CKB(3.0.1)"+
					", CRT(2.4.0), CTX(3.2.3), DPL(1.5.0), MDS(0.4.5))");
			System.exit(0);
		}
		
		//Create an instance of the main MW class
		MwImpl mw = new MwImpl();
			
		//Start the middleware
		int res = mw.startMiddleware();				
		
		if (res < 0) {
			//Failure, report error as fatal
			//... do something
			CfgError.errorReporting(CfgError.ERR_OPER, "MW_Core", CfgError.SEV_FATAL, 
					"main(), Failed to start the middleware, errno="+res+" - terminating", null);
			System.exit(res);

		}
	}
}
